#ifndef BUG_H_
#define BUG_H_

#include <fstream>
#include <string>
#include <ctime>
#include <sstream>
#include <sys/types.h>
#include <unistd.h>
#include <iomanip>


#ifndef DEBUG
    //#define DEBUG
#endif

/*
    struct for debugging - this is gross but can be used pretty much like an ofstream,
                           except the debug messages are stripped while compiling if
                           DEBUG is not defined.
    example:
        Bug bug;
        bug.open("./debug.txt");
        bug << state << endl;
        bug << "testing" << 2.0 << '%' << endl;
        bug.close();
*/
struct Bug
{
    std::ofstream file;

  Bug()
  {
    
  }
 
  Bug(const char * filename)
  {
    init(filename);
  }

  void init(const char * filename)
  {
#ifdef DEBUG
    // suffixes file by PID
    std::ostringstream fname;
    fname << filename << "_" << getpid() << ".txt";
    file.open(fname.str().c_str());
#endif
  }


  ~Bug()
  {
#ifdef DEBUG
    file.close();
#endif
  }

};

//output function for endl
inline Bug& operator<<(Bug &bug, std::ostream& (*manipulator)(std::ostream&))
{
    #ifdef DEBUG
        bug.file << manipulator;
    #endif

    return bug;
}

//output function
template <class T>
inline Bug& operator<<(Bug &bug, const T &t)
{
#ifdef DEBUG
  bug.file << t;
#endif
  
  return bug;
}

extern Bug bug;

#ifdef DEBUG

#define ASSERT(EXP) \
do { if (!((EXP))) {  \
        bug << "Assert failed ! " << #EXP << ", " << __FILE__ << ", line " << __LINE__ << std::endl; \
    } } while (0)

class DEBUGTracker
{
private:
    std::string where;
    static int depth;
public:
    DEBUGTracker(const char *filename, const char * func, unsigned line) : where(func)
    {
        bug << std::string(depth, '-') << " Enter " << where << std::endl;
        ++depth;
    }
    ~DEBUGTracker() {
        --depth;
        bug << std::string(depth, '-') << " Exit " << where << std::endl;
    }
};

/// use TRACK(); macro at the beginning of a function to track Enter/Exit events
#define TRACK() DEBUGTracker __tracker(__FILE__, __func__, __LINE__)

#else

#define ASSERT(EXP)
#define TRACK()

#endif

#endif //BUG_H_
